package ru.spb.osll.myfit.server.execute;

import fit.Fixture;
import fit.FixtureLoader;
import fit.FixtureName;
import fit.exception.CouldNotLoadComponentFitFailureException;
import fit.exception.NoSuchFixtureException;
import util.GracefulNamer;

import java.lang.reflect.Method;
import java.net.URLClassLoader;
import java.util.jar.JarFile;

public class MyFitFixtureLoader extends FixtureLoader {
  private ClassLoader m_classLoader;

  public MyFitFixtureLoader(ClassLoader classLoader) {
    this.m_classLoader = classLoader;
  }

  @Override
  public Fixture disgraceThenLoad(String tableName) throws Exception {
    FixtureName fixtureName = new FixtureName(GracefulNamer.disgrace(tableName));
    Fixture fixture = instantiateFirstValidFixtureClass(fixtureName);
    addPackageToFixturePath(fixture);
    return fixture;
  }

  protected void addPackageToFixturePath(Fixture fixture) {
    Package fixturePackage = fixture.getClass().getPackage();
    if (fixturePackage != null) {
      FixtureLoader.instance().addPackageToPath(fixturePackage.getName());
    }
  }

  protected Fixture instantiateFirstValidFixtureClass(FixtureName fixtureName)
          throws Exception {
    for (String each : fixtureName.getPotentialFixtureClassNames(FixtureLoader.instance().fixturePathElements)) {
      try {
        return instantiateFixture(each);
      }
      catch (NoSuchFixtureException ignoreAndTryTheNextCandidate) {
      }
    }

    throw new NoSuchFixtureException(fixtureName.toString());
  }

  protected Fixture instantiateFixture(String fixtureName) throws Exception {
    Class<?> classForFixture = loadFixtureClass(fixtureName);
    Object obj = classForFixture.newInstance();
    Fixture fixture = (Fixture) obj;
    return fixture;

  }

  protected Class<?> loadFixtureClass(String fixtureName) {
    try {
      return (Class<?>) Class.forName(fixtureName, true, m_classLoader);
    } catch (ClassNotFoundException deadEnd) {
      if (deadEnd.getMessage().equals(fixtureName))
        throw new NoSuchFixtureException(fixtureName);
      else
        throw new CouldNotLoadComponentFitFailureException(
                deadEnd.getMessage(), fixtureName);
    }
  }

  public void close() {
    try {
      Class<URLClassLoader> clazz = java.net.URLClassLoader.class;
      java.lang.reflect.Field ucp = clazz.getDeclaredField("ucp");
      ucp.setAccessible(true);
      Object sun_misc_URLClassPath = ucp.get(m_classLoader);
      java.lang.reflect.Field loaders = sun_misc_URLClassPath.getClass()
              .getDeclaredField("loaders");
      loaders.setAccessible(true);
      Object java_util_Collection = loaders.get(sun_misc_URLClassPath);
      for (Object sun_misc_URLClassPath_JarLoader : ((java.util.Collection<?>) java_util_Collection)
              .toArray()) {
        try {

          java.lang.reflect.Field loader = sun_misc_URLClassPath_JarLoader
                  .getClass().getDeclaredField("jar");
          loader.setAccessible(true);
          Object java_util_jar_JarFile = loader
                  .get(sun_misc_URLClassPath_JarLoader);
          if (java_util_jar_JarFile instanceof JarFile) {
            ((java.util.jar.JarFile) java_util_jar_JarFile).close();
          }

        } catch (Throwable t) {
        }
      }

      Class<?> MyFitLoggerClazz = m_classLoader.loadClass("ru.spb.osll.myfit.util.logger.MyFitLogger");
      Method method = MyFitLoggerClazz.getMethod("closeAppenders");
      Object obj = MyFitLoggerClazz.newInstance();
      method.invoke(obj);
    } catch (Throwable t) {
    }
    return;
  }
}
